/*
 * Copyright (C) 2008 - 2010 Trinity <http://www.trinitycore.org/>
 *
 * Copyright (C) 2010 Myth Project <http://mythproject.org/>
 *
 * Copyright (C) 2006 - 2010 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
 *
 * Modified by Attilio Scali
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

 /* ScriptData
 SDName: boss_Lich_king
 SD%Complete: 80%
 SDComment: new script for tc implementation.
 SDCategory: Halls of Reflection
 EndScriptData */

#include "ScriptPCH.h"
#include "halls_of_reflection.h"
#include "ScriptedEscortAI.h"

enum
{
  SPELL_WINTER                       = 69780,
  SPELL_FURY_OF_FROSTMOURNE          = 70063,
  SPELL_SOUL_REAPER                  = 73797,
  SPELL_RAISE_DEAD                   = 69818,
  SPELL_ICE_PRISON                   = 69708,
  SPELL_DARK_ARROW                   = 70194,
  SPELL_EMERGE_VISUAL                = 50142,
  SPELL_DESTROY_ICE_WALL_02          = 70224,
  SPELL_SILENCE                      = 69413,
  SPELL_LICH_KING_CAST               = 57561,
  SPELL_GNOUL_JUMP                   = 70150,
  SPELL_ABON_STRIKE                  = 40505,

  SPELL_TAKE_FROSTMOURNE             = 72729,
  SPELL_FROSTMOURNE_VISUAL           = 73220,

  SAY_LICH_KING_WALL_01              = -1594486,
  SAY_LICH_KING_WALL_02              = -1594491,
  SAY_LICH_KING_GNOUL                = -1594482,
  SAY_LICH_KING_ABON                 = -1594483,
  SAY_LICH_KING_WINTER               = -1594481,
  SAY_LICH_KING_END_RUN              = -1594504,
  SAY_LICH_KING_WIN                  = -1594485,
};

class boss_lich_king_hor : public CreatureScript
{
public:
    boss_lich_king_hor() : CreatureScript("boss_lich_king_hor") { }

    struct boss_lich_king_hrAI : public npc_escortAI
    {
	   boss_lich_king_hrAI(Creature *pCreature) : npc_escortAI(pCreature)
       {
            m_pInstance = (InstanceScript*)pCreature->GetInstanceScript();
            Reset();
       }

       InstanceScript* m_pInstance;
       uint32 Step;
       uint32 StepTimer;
       bool StartEscort;;
       bool NonFight;
       bool Finish;

       void Reset()
       {

          if(!m_pInstance)
		  {
			  return;
		  }
          NonFight = false;
          StartEscort = false;
		  me->CastSpell(me, SPELL_TAKE_FROSTMOURNE, false);
		  me->CastSpell(me, SPELL_FROSTMOURNE_VISUAL, false);
       }

       void JustDied(Unit* pKiller)
       {
       }

       void WaypointReached(uint32 i)
       {
            switch(i)
            {
                case 20:
                    SetEscortPaused(true);
                    Finish = true;
                    DoCast(me, SPELL_LICH_KING_CAST);
                    m_pInstance->SetData(TYPE_LICH_KING, SPECIAL);
                    DoScriptText(SAY_LICH_KING_END_RUN, me);
                    break;
            }
       }

       void AttackStart(Unit* who)
       {
          if (!m_pInstance) return;
          if (!who)        return;

         if (NonFight) return;

         if(m_pInstance->GetData(TYPE_LICH_KING) == IN_PROGRESS || who->GetTypeId() == TYPEID_PLAYER) return;

         npc_escortAI::AttackStart(who);
       }

       void JustSummoned(Creature* summoned)
       {
             if(!m_pInstance || !summoned) return;
			 m_pInstance->SetData(DATA_SUMMONS, 1);
       }

	   void SummonGhoul()
	   {
		   for(int i=0; i<=4; i++)
			   me->SummonCreature(NPC_RAGING_GNOUL,(me->GetPositionX()-5)+rand()%10, (me->GetPositionY()-5)+rand()%10, me->GetPositionZ(),4.17f,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,360000);
	   }
                

       void Wall01()
       {
          switch(Step)
          {
             case 0:
                SetEscortPaused(true);
                m_pInstance->SetData(DATA_SUMMONS, 3);
                DoScriptText(SAY_LICH_KING_WALL_01, me);
                DoCast(me, SPELL_DESTROY_ICE_WALL_02);
                StepTimer = 2000;
                ++Step;
                break;
             case 1:
                StepTimer = 2000;
                ++Step;
                break;
             case 2:
                //DoCast(me, SPELL_RAISE_DEAD); //not always work so do it manually:
				SummonGhoul();
                DoScriptText(SAY_LICH_KING_GNOUL, me);
                StepTimer = 7000;
                ++Step;
                break;
             case 3:
				me->SetSpeed(MOVE_WALK,0.75f,true);
                DoCast(me, SPELL_WINTER);
                DoScriptText(SAY_LICH_KING_WINTER, me);
                StepTimer = 10000;
                ++Step;
                break;
             case 4:
                SetEscortPaused(false);
                StepTimer = 2000;
                ++Step;
                break;
             case 5:
                me->SummonCreature(NPC_RISEN_WITCH_DOCTOR,(me->GetPositionX()-5)+rand()%10, (me->GetPositionY()-5)+rand()%10, me->GetPositionZ(),4.17f,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,360000);
                m_pInstance->SetData(TYPE_ICE_WALL_01, DONE);
                StepTimer = 100;
                Step = 0;
                break;
           }
       }

       void Wall02()
       {
          switch(Step)
          {
              case 0:
                m_pInstance->SetData(DATA_SUMMONS, 3);
                SetEscortPaused(true);
                //DoCast(me, SPELL_RAISE_DEAD); //not always work so do it manually:
				SummonGhoul();
                DoScriptText(SAY_LICH_KING_GNOUL, me);
                StepTimer = 10000;
                ++Step;
                break;
              case 1:
                SetEscortPaused(false);
                me->SummonCreature(NPC_RISEN_WITCH_DOCTOR,(me->GetPositionX()-5)+rand()%10, (me->GetPositionY()-5)+rand()%10, me->GetPositionZ(),4.17f,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,360000);
                me->SummonCreature(NPC_RISEN_WITCH_DOCTOR,(me->GetPositionX()-5)+rand()%10, (me->GetPositionY()-5)+rand()%10, me->GetPositionZ(),4.17f,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,360000);
                me->SummonCreature(NPC_ABON,(me->GetPositionX()-5)+rand()%10, (me->GetPositionY()-5)+rand()%10, me->GetPositionZ(),4.17f,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,360000);
                m_pInstance->SetData(TYPE_ICE_WALL_02, DONE);
                StepTimer = 100;
                Step = 0;
                break;
          }
       }

       void Wall03()
       {
          switch(Step)
          {
             case 0:
               m_pInstance->SetData(DATA_SUMMONS, 3);
               SetEscortPaused(true);
               //DoCast(me, SPELL_RAISE_DEAD); //not always work so do it manually:
			   SummonGhoul();
               DoScriptText(SAY_LICH_KING_GNOUL, me);
               StepTimer = 10000;
               ++Step;
               break;
             case 1:
               SetEscortPaused(false);
               DoScriptText(SAY_LICH_KING_ABON, me);
               me->SummonCreature(NPC_RISEN_WITCH_DOCTOR,(me->GetPositionX()-5)+rand()%10, (me->GetPositionY()-5)+rand()%10, me->GetPositionZ(),4.17f,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,360000);
               me->SummonCreature(NPC_RISEN_WITCH_DOCTOR,(me->GetPositionX()-5)+rand()%10, (me->GetPositionY()-5)+rand()%10, me->GetPositionZ(),4.17f,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,360000);
               me->SummonCreature(NPC_RISEN_WITCH_DOCTOR,(me->GetPositionX()-5)+rand()%10, (me->GetPositionY()-5)+rand()%10, me->GetPositionZ(),4.17f,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,360000);
               me->SummonCreature(NPC_ABON,(me->GetPositionX()-5)+rand()%10, (me->GetPositionY()-5)+rand()%10, me->GetPositionZ(),4.17f,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,360000);
               me->SummonCreature(NPC_ABON,(me->GetPositionX()-5)+rand()%10, (me->GetPositionY()-5)+rand()%10, me->GetPositionZ(),4.17f,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,360000);
               m_pInstance->SetData(TYPE_ICE_WALL_03, DONE);
               StepTimer = 100;
               Step = 0;
               break;
          }
       }

       void Wall04()
       {
          switch(Step)
          {
             case 0:
               m_pInstance->SetData(DATA_SUMMONS, 3);
               SetEscortPaused(true);
               //DoCast(me, SPELL_RAISE_DEAD); //not always work so do it manually:
			   SummonGhoul();
               DoScriptText(SAY_LICH_KING_GNOUL, me);
               StepTimer = 10000;
               ++Step;
               break;
             case 1:
			   SetEscortPaused(false);
               me->SummonCreature(NPC_RISEN_WITCH_DOCTOR,(me->GetPositionX()-5)+rand()%10, (me->GetPositionY()-5)+rand()%10, me->GetPositionZ(),4.17f,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,360000);
               me->SummonCreature(NPC_RISEN_WITCH_DOCTOR,(me->GetPositionX()-5)+rand()%10, (me->GetPositionY()-5)+rand()%10, me->GetPositionZ(),4.17f,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,360000);
               me->SummonCreature(NPC_RISEN_WITCH_DOCTOR,(me->GetPositionX()-5)+rand()%10, (me->GetPositionY()-5)+rand()%10, me->GetPositionZ(),4.17f,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,360000);
               me->SummonCreature(NPC_ABON,(me->GetPositionX()-5)+rand()%10, (me->GetPositionY()-5)+rand()%10, me->GetPositionZ(),4.17f,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,360000);
               me->SummonCreature(NPC_ABON,(me->GetPositionX()-5)+rand()%10, (me->GetPositionY()-5)+rand()%10, me->GetPositionZ(),4.17f,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,360000);
               StepTimer = 15000;
               ++Step;
               break;
             case 2:
               DoScriptText(SAY_LICH_KING_ABON, me);
               me->SummonCreature(NPC_RISEN_WITCH_DOCTOR,(me->GetPositionX()-5)+rand()%10, (me->GetPositionY()-5)+rand()%10, me->GetPositionZ(),4.17f,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,360000);
               me->SummonCreature(NPC_RISEN_WITCH_DOCTOR,(me->GetPositionX()-5)+rand()%10, (me->GetPositionY()-5)+rand()%10, me->GetPositionZ(),4.17f,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,360000);
               m_pInstance->SetData(TYPE_ICE_WALL_04, DONE);
               ++Step;
               break;
          }
       }

       void UpdateEscortAI(const uint32 diff)
        {
          if(!m_pInstance) 
		  {
			  return;
		  }

		  if(m_pInstance->GetData(TYPE_LICH_KING) == DONE)
			  me->DisappearAndDie();

          if(m_pInstance->GetData(TYPE_LICH_KING) != IN_PROGRESS)
          {
             if (!UpdateVictim())
			 {
				 return;
			 }

             DoMeleeAttackIfReady();
          }

          if(me->isInCombat() && m_pInstance->GetData(TYPE_LICH_KING) == IN_PROGRESS)
		  {
			  npc_escortAI::EnterEvadeMode();
		  }

		  if(m_pInstance->GetData(TYPE_LICH_KING) == IN_PROGRESS && StartEscort != true)
          {
             StartEscort = true;
             if(me->HasAura(SPELL_ICE_PRISON))
                me->RemoveAurasDueToSpell(SPELL_ICE_PRISON);
             if(me->HasAura(SPELL_DARK_ARROW))
                me->RemoveAurasDueToSpell(SPELL_DARK_ARROW);


             NonFight = true;
             me->AttackStop();
			 me->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
             me->SetSpeed(MOVE_RUN, 1.0f, true);
             if (boss_lich_king_hrAI* pEscortAI = dynamic_cast<boss_lich_king_hrAI*>(me->AI()))
                 pEscortAI->Start(true, true);
             Step = 0;
             StepTimer = 100;
          }

          if (Creature* pLider = ((Creature*)Unit::GetUnit((*me), m_pInstance->GetData64(DATA_ESCAPE_LIDER))))
             if (pLider->IsWithinDistInMap(me, 2.0f))
             {
                me->setActive(false);
                SetEscortPaused(true);
                npc_escortAI::EnterEvadeMode();
                DoScriptText(SAY_LICH_KING_WIN, me);
                me->CastSpell(me, SPELL_FURY_OF_FROSTMOURNE, false);
                me->DealDamage(pLider, pLider->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
                me->NearTeleportTo(5572.077f, 2283.1f, 734.976f, 3.89f);
                m_pInstance->SetData(TYPE_LICH_KING, FAIL);
             };

          if(m_pInstance->GetData(TYPE_ICE_WALL_01) == IN_PROGRESS)
          {
             if(StepTimer < diff)
                Wall01();
             else StepTimer -= diff;
          }

          if(m_pInstance->GetData(TYPE_ICE_WALL_02) == IN_PROGRESS)
          {
             if(StepTimer < diff)
                Wall02();
             else StepTimer -= diff;
          }

          if(m_pInstance->GetData(TYPE_ICE_WALL_03) == IN_PROGRESS)
          {
             if(StepTimer < diff)
                Wall03();
             else StepTimer -= diff;
          }

          if(m_pInstance->GetData(TYPE_ICE_WALL_04) == IN_PROGRESS)
          {
             if(StepTimer < diff)
                Wall04();
             else StepTimer -= diff;
          }
          return;
        }
    };

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new boss_lich_king_hrAI(pCreature);
    }
};



enum TrashSpells
{
    // Ghostly Priest
    SPELL_SHADOW_WORD_PAIN                        = 72318,
    SPELL_CIRCLE_OF_DESTRUCTION                   = 72320,
    SPELL_COWER_IN_FEAR                           = 72321,
    SPELL_DARK_MENDING                            = 72322,

    // Phantom Mage
    SPELL_FIREBALL                                = 72163,
    SPELL_FLAMESTRIKE                             = 72169,
    SPELL_FROSTBOLT                               = 72166,
    SPELL_CHAINS_OF_ICE                           = 72121,
    SPELL_HALLUCINATION                           = 72342,

    // Phantom Hallucination (same as phantom mage + HALLUCINATION_2 when dies)
    SPELL_HALLUCINATION_2                         = 72344,

    // Shadowy Mercenary
    SPELL_SHADOW_STEP                             = 72326,
    SPELL_DEADLY_POISON                           = 72329,
    SPELL_ENVENOMED_DAGGER_THROW                  = 72333,
    SPELL_KIDNEY_SHOT                             = 72335,

    // Spectral Footman
    SPELL_SPECTRAL_STRIKE                         = 72198,
    SPELL_SHIELD_BASH                             = 72194,
    SPELL_TORTURED_ENRAGE                         = 72203,

    // Tortured Rifleman
    SPELL_SHOOT                                   = 72208,
    SPELL_CURSED_ARROW                            = 72222,
    SPELL_FROST_TRAP                              = 72215,
    SPELL_ICE_SHOT                                = 72268,

	//Raging Ghoul
	SPELL_LEAP									  = 70150,

	//Risen Witch Doctor
	SPELL_CURSE_OF_DOOM						      = 70144,
	SPELL_SHADOW_BOLT							  = 70080,
	SPELL_SHADOW_BOLT_VOLLEY					  = 70145,

	//Lumbering Abomination
	SPELL_CLEAVE								  = 40505,
	SPELL_VOMIT_SPRAY							  = 70176 //solo heroic
};

enum TrashEvents
{
    EVENT_TRASH_NONE,

    // Ghostly Priest
    EVENT_SHADOW_WORD_PAIN,
    EVENT_CIRCLE_OF_DESTRUCTION,
    EVENT_COWER_IN_FEAR,
    EVENT_DARK_MENDING,

    // Phantom Mage
    EVENT_FIREBALL,
    EVENT_FLAMESTRIKE,
    EVENT_FROSTBOLT,
    EVENT_CHAINS_OF_ICE,
    EVENT_HALLUCINATION,

    // Shadowy Mercenary
    EVENT_SHADOW_STEP,
    EVENT_DEADLY_POISON,
    EVENT_ENVENOMED_DAGGER_THROW,
    EVENT_KIDNEY_SHOT,

    // Spectral Footman
    EVENT_SPECTRAL_STRIKE,
    EVENT_SHIELD_BASH,
    EVENT_TORTURED_ENRAGE,

    // Tortured Rifleman
    EVENT_SHOOT,
    EVENT_CURSED_ARROW,
    EVENT_FROST_TRAP,
    EVENT_ICE_SHOT,

	//Raging Ghoul
	EVENT_LEAP,

	//Risen Witch Doctor
	EVENT_CURSE_OF_DOOM,
	EVENT_SHADOW_BOLT,
	EVENT_SHADOW_BOLT_VOLLEY,

	//Lumbering Abomination
	EVENT_CLEAVE,
	EVENT_VOMIT_SPRAY, //solo heroic
};

class npc_ghostly_priest : public CreatureScript
{
public:
    npc_ghostly_priest() : CreatureScript("npc_ghostly_priest") { }

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new npc_ghostly_priestAI(pCreature);
    }

    struct npc_ghostly_priestAI: public ScriptedAI
    {
        npc_ghostly_priestAI(Creature *c) : ScriptedAI(c)
        {
        }

        EventMap events;

        void Reset()
        {
            events.Reset();
        }

        void EnterCombat(Unit* /*who*/)
        {
            events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 5000);
            events.ScheduleEvent(EVENT_CIRCLE_OF_DESTRUCTION, 12000);
            events.ScheduleEvent(EVENT_COWER_IN_FEAR, 10000);
            events.ScheduleEvent(EVENT_DARK_MENDING, 20000);
        }

        void UpdateAI(const uint32 diff)
        {
            if (!UpdateVictim())
                return;

            events.Update(diff);

            if (me->hasUnitState(UNIT_STAT_CASTING))
                return;

            while (uint32 eventId = events.ExecuteEvent())
            {
                switch(eventId)
                {
                    case EVENT_SHADOW_WORD_PAIN:
                        if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                            DoCast(pTarget, SPELL_SHADOW_WORD_PAIN);
                        events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 20000);
                        return;
                    case EVENT_CIRCLE_OF_DESTRUCTION:
                        if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                            DoCast(pTarget, SPELL_CIRCLE_OF_DESTRUCTION);
                        events.ScheduleEvent(EVENT_CIRCLE_OF_DESTRUCTION, 12000);
                        return;
                    case EVENT_COWER_IN_FEAR:
                        if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                            DoCast(pTarget, SPELL_COWER_IN_FEAR);
                        events.ScheduleEvent(EVENT_COWER_IN_FEAR, 10000);
                        return;
                    case EVENT_DARK_MENDING:
                        // find an ally with missing HP
                        if (Unit *pTarget = DoSelectLowestHpFriendly(40, DUNGEON_MODE(30000,50000)))
                        {
                            DoCast(pTarget, SPELL_DARK_MENDING);
                            events.ScheduleEvent(EVENT_DARK_MENDING, 20000);
                        }
                        else
                        {
                            // no friendly unit with missing hp. re-check in just 5 sec.
                            events.ScheduleEvent(EVENT_DARK_MENDING, 5000);
                        }
                        return;
                }
            }

            DoMeleeAttackIfReady();
        }
    };

};
class npc_phantom_mage : public CreatureScript
{
public:
    npc_phantom_mage() : CreatureScript("npc_phantom_mage") { }

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new npc_phantom_mageAI(pCreature);
    }

    struct npc_phantom_mageAI: public ScriptedAI
    {
        npc_phantom_mageAI(Creature *c) : ScriptedAI(c)
        {
        }

        EventMap events;

        void Reset()
        {
            events.Reset();
        }

        void EnterCombat(Unit* /*who*/)
        {
            events.ScheduleEvent(EVENT_FIREBALL, 3000); // TODO: adjust timers
            events.ScheduleEvent(EVENT_FLAMESTRIKE, 6000);
            events.ScheduleEvent(EVENT_FROSTBOLT, 9000);
            events.ScheduleEvent(EVENT_CHAINS_OF_ICE, 12000);
            events.ScheduleEvent(EVENT_HALLUCINATION, 40000);
        }

        void UpdateAI(const uint32 diff)
        {
            if (!UpdateVictim())
                return;

            events.Update(diff);

            if (me->hasUnitState(UNIT_STAT_CASTING))
                return;

            while (uint32 eventId = events.ExecuteEvent())
            {
                switch(eventId)
                {
                    case EVENT_FIREBALL:
                        if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                            DoCast(pTarget, SPELL_FIREBALL);
                        events.ScheduleEvent(EVENT_FIREBALL, 15000);
                        return;
                    case EVENT_FLAMESTRIKE:
                        DoCast(SPELL_FLAMESTRIKE);
                        events.ScheduleEvent(EVENT_FLAMESTRIKE, 15000);
                        return;
                    case EVENT_FROSTBOLT:
                        if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                            DoCast(pTarget, SPELL_FROSTBOLT);
                        events.ScheduleEvent(EVENT_FROSTBOLT, 15000);
                        return;
                    case EVENT_CHAINS_OF_ICE:
                        if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                            DoCast(pTarget, SPELL_CHAINS_OF_ICE);
                        events.ScheduleEvent(EVENT_CHAINS_OF_ICE, 15000);
                        return;
                    case EVENT_HALLUCINATION:
                        DoCast(SPELL_HALLUCINATION);
                        return;
                }
            }

            DoMeleeAttackIfReady();
        }
    };

};

class npc_phantom_hallucination : public CreatureScript
{
public:
    npc_phantom_hallucination() : CreatureScript("npc_phantom_hallucination") { }

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new npc_phantom_hallucinationAI(pCreature);
    }

    struct npc_phantom_hallucinationAI : public npc_phantom_mage::npc_phantom_mageAI
    {
        npc_phantom_hallucinationAI(Creature *c) : npc_phantom_mage::npc_phantom_mageAI(c)
        {
        }

        void JustDied(Unit * /*pWho*/)
        {
            DoCast(SPELL_HALLUCINATION_2);
        }
    };

};

class npc_shadowy_mercenary : public CreatureScript
{
public:
    npc_shadowy_mercenary() : CreatureScript("npc_shadowy_mercenary") { }

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new npc_shadowy_mercenaryAI(pCreature);
    }

    struct npc_shadowy_mercenaryAI: public ScriptedAI
    {
        npc_shadowy_mercenaryAI(Creature *c) : ScriptedAI(c)
        {
        }

        EventMap events;

        void Reset()
        {
            events.Reset();
        }

        void EnterCombat(Unit* /*who*/)
        {
            events.ScheduleEvent(EVENT_SHADOW_STEP, 8000); // TODO: adjust timers
            events.ScheduleEvent(EVENT_DEADLY_POISON, 5000);
            events.ScheduleEvent(EVENT_ENVENOMED_DAGGER_THROW, 10000);
            events.ScheduleEvent(EVENT_KIDNEY_SHOT, 12000);
        }

        void UpdateAI(const uint32 diff)
        {
            if (!UpdateVictim())
                return;

            events.Update(diff);

            if (me->hasUnitState(UNIT_STAT_CASTING))
                return;

            while (uint32 eventId = events.ExecuteEvent())
            {
                switch(eventId)
                {
                    case EVENT_SHADOW_STEP:
                        DoCast(SPELL_SHADOW_STEP);
                        events.ScheduleEvent(EVENT_SHADOW_STEP, 8000);
                        return;
                    case EVENT_DEADLY_POISON:
                        DoCast(me->getVictim(), SPELL_DEADLY_POISON);
                        events.ScheduleEvent(EVENT_DEADLY_POISON, 10000);
                        return;
                    case EVENT_ENVENOMED_DAGGER_THROW:
                        if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                            DoCast(pTarget, SPELL_ENVENOMED_DAGGER_THROW);
                        events.ScheduleEvent(EVENT_ENVENOMED_DAGGER_THROW, 10000);
                        return;
                    case EVENT_KIDNEY_SHOT:
                        DoCast(me->getVictim(), SPELL_KIDNEY_SHOT);
                        events.ScheduleEvent(EVENT_KIDNEY_SHOT, 10000);
                        return;
                }
            }

            DoMeleeAttackIfReady();
        }
    };

};

class npc_spectral_footman : public CreatureScript
{
public:
    npc_spectral_footman() : CreatureScript("npc_spectral_footman") { }

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new npc_spectral_footmanAI(pCreature);
    }

    struct npc_spectral_footmanAI: public ScriptedAI
    {
        npc_spectral_footmanAI(Creature *c) : ScriptedAI(c)
        {
        }

        EventMap events;

        void Reset()
        {
            events.Reset();
        }

        void EnterCombat(Unit* /*who*/)
        {
            events.ScheduleEvent(EVENT_SPECTRAL_STRIKE, 5000); // TODO: adjust timers
            events.ScheduleEvent(EVENT_SHIELD_BASH, 10000);
            events.ScheduleEvent(EVENT_TORTURED_ENRAGE, 15000);
        }

        void UpdateAI(const uint32 diff)
        {
            if (!UpdateVictim())
                return;

            events.Update(diff);

            if (me->hasUnitState(UNIT_STAT_CASTING))
                return;

            while (uint32 eventId = events.ExecuteEvent())
            {
                switch(eventId)
                {
                    case EVENT_SPECTRAL_STRIKE:
                        DoCast(me->getVictim(), SPELL_SPECTRAL_STRIKE);
                        events.ScheduleEvent(EVENT_SPECTRAL_STRIKE, 5000);
                        return;
                    case EVENT_SHIELD_BASH:
                        DoCast(me->getVictim(), SPELL_SHIELD_BASH);
                        events.ScheduleEvent(EVENT_SHIELD_BASH, 5000);
                        return;
                    case EVENT_TORTURED_ENRAGE:
                        DoCast(SPELL_TORTURED_ENRAGE);
                        events.ScheduleEvent(EVENT_TORTURED_ENRAGE, 15000);
                        return;
                }
            }

            DoMeleeAttackIfReady();
        }
    };

};

class npc_tortured_rifleman : public CreatureScript
{
public:
    npc_tortured_rifleman() : CreatureScript("npc_tortured_rifleman") { }

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new npc_tortured_riflemanAI(pCreature);
    }

    struct npc_tortured_riflemanAI  : public ScriptedAI
    {
        npc_tortured_riflemanAI(Creature *c) : ScriptedAI(c)
        {
        }

        EventMap events;

        void Reset()
        {
            events.Reset();
        }

        void EnterCombat(Unit* /*who*/)
        {
            events.ScheduleEvent(EVENT_SHOOT, 2000); // TODO: adjust timers
            events.ScheduleEvent(EVENT_CURSED_ARROW, 10000);
            events.ScheduleEvent(EVENT_FROST_TRAP, 1000);
            events.ScheduleEvent(EVENT_ICE_SHOT, 15000);
        }

        void UpdateAI(const uint32 diff)
        {
            if (!UpdateVictim())
                return;

            events.Update(diff);

            if (me->hasUnitState(UNIT_STAT_CASTING))
                return;

            while (uint32 eventId = events.ExecuteEvent())
            {
                switch(eventId)
                {
                    case EVENT_SHOOT:
                        if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                            DoCast(pTarget, SPELL_SHOOT);
                        events.ScheduleEvent(EVENT_SHOOT, 2000);
                        return;
                    case EVENT_CURSED_ARROW:
                        if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                            DoCast(pTarget, SPELL_CURSED_ARROW);
                        events.ScheduleEvent(EVENT_CURSED_ARROW, 10000);
                        return;
                    case EVENT_FROST_TRAP:
                        DoCast(SPELL_FROST_TRAP);
                        events.ScheduleEvent(EVENT_FROST_TRAP, 30000);
                        return;
                    case EVENT_ICE_SHOT:
                        if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                            DoCast(pTarget, SPELL_ICE_SHOT);
                        events.ScheduleEvent(EVENT_ICE_SHOT, 15000);
                        return;
                }
            }

            DoMeleeAttackIfReady();
        }
    };

};

class npc_raging_gnoul : public CreatureScript
{
public:
    npc_raging_gnoul() : CreatureScript("npc_raging_gnoul") { }

    struct npc_raging_gnoulAI : public ScriptedAI
    {
		npc_raging_gnoulAI(Creature *pCreature) : ScriptedAI(pCreature)
        {
			m_pInstance = (InstanceScript*)pCreature->GetInstanceScript();
            Reset();
        }
		
		InstanceScript* m_pInstance;
		EventMap events;

        void Reset()
        {
			me->SetReactState(REACT_AGGRESSIVE);
			me->SetInCombatWithZone();
			events.Reset();
		}

		void EnterCombat(Unit* /*who*/)
        {
            events.ScheduleEvent(EVENT_LEAP, 5000);
        }

        void JustDied(Unit* pKiller)
        {
			if(!m_pInstance) return;
            m_pInstance->SetData(DATA_SUMMONS, 0);
        }

		void UpdateAI(const uint32 diff)
        {
			if (!UpdateVictim())
                return;

			events.Update(diff);

			while (uint32 eventId = events.ExecuteEvent())
            {
                switch(eventId)
                {
                    case EVENT_LEAP:
						DoCast(me->getVictim(), SPELL_LEAP);
                        events.ScheduleEvent(EVENT_LEAP, 5000);
                        return;
				}
			}
			
			DoMeleeAttackIfReady();
        }
    };

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new npc_raging_gnoulAI(pCreature);
    }

};

class npc_risen_witch_doctor : public CreatureScript
{
public:
    npc_risen_witch_doctor() : CreatureScript("npc_risen_witch_doctor") { }

    struct npc_risen_witch_doctorAI : public ScriptedAI
    {
		npc_risen_witch_doctorAI(Creature *pCreature) : ScriptedAI(pCreature)
		{
            m_pInstance = (InstanceScript*)pCreature->GetInstanceScript();
            Reset();
        }

		InstanceScript* m_pInstance;
		EventMap events;

        void Reset()
        {
			me->SetReactState(REACT_AGGRESSIVE);
			me->SetInCombatWithZone();
			events.Reset();
		}

		void EnterCombat(Unit* /*who*/)
        {
            events.ScheduleEvent(EVENT_CURSE_OF_DOOM, 7000);
			events.ScheduleEvent(EVENT_SHADOW_BOLT, 5000);
			events.ScheduleEvent(EVENT_SHADOW_BOLT_VOLLEY, 18000);
        }

        void JustDied(Unit* pKiller)
        {
			if(!m_pInstance) return;
            m_pInstance->SetData(DATA_SUMMONS, 0);
        }

        void UpdateAI(const uint32 diff)
        {
			if (!UpdateVictim())
                return;

			events.Update(diff);

            if (me->hasUnitState(UNIT_STAT_CASTING))
                return;

			while (uint32 eventId = events.ExecuteEvent())
            {
                switch(eventId)
                {
                    case EVENT_CURSE_OF_DOOM:
						DoCast(me->getVictim(), SPELL_CURSE_OF_DOOM);
                        events.ScheduleEvent(EVENT_CURSE_OF_DOOM, RAND(5000,7000));
                        return;
					case EVENT_SHADOW_BOLT:
						DoCast(me->getVictim(), SPELL_SHADOW_BOLT);
                        events.ScheduleEvent(EVENT_SHADOW_BOLT, RAND(5000,8000));
                        return;
					case EVENT_SHADOW_BOLT_VOLLEY:
						if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
                            DoCast(pTarget, SPELL_SHADOW_BOLT_VOLLEY);
                        events.ScheduleEvent(EVENT_SHADOW_BOLT_VOLLEY, RAND(10000,18000));
                        return;
				}
			}

			DoMeleeAttackIfReady();
		}
    };

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new npc_risen_witch_doctorAI(pCreature);
    }

};

class npc_abon : public CreatureScript
{
public:
    npc_abon() : CreatureScript("npc_abon") { }

    struct npc_abonAI : public ScriptedAI
    {
        npc_abonAI(Creature *pCreature) : ScriptedAI(pCreature)
        {
            m_pInstance = (InstanceScript*)pCreature->GetInstanceScript();
			Regular = pCreature->GetMap()->IsRegularDifficulty();
            Reset();
        }

		InstanceScript* m_pInstance;
		EventMap events;
		bool Regular;

        void Reset()
        {
			me->SetReactState(REACT_AGGRESSIVE);
			me->SetInCombatWithZone();
			events.Reset();
		}

		void EnterCombat(Unit* /*who*/)
        {
            events.ScheduleEvent(EVENT_CLEAVE, 5000);
			events.ScheduleEvent(EVENT_VOMIT_SPRAY, 7000);
        }

		void UpdateAI(const uint32 diff)
        {
			if (!UpdateVictim())
                return;

			events.Update(diff);

            if (me->hasUnitState(UNIT_STAT_CASTING))
                return;

			while (uint32 eventId = events.ExecuteEvent())
            {
                switch(eventId)
                {
                    case EVENT_CLEAVE:
						DoCast(me->getVictim(), SPELL_CLEAVE);
                        events.ScheduleEvent(EVENT_CLEAVE, RAND(5000,7000));
                        return;
					case EVENT_VOMIT_SPRAY:
						if(!Regular) //only in heroic mode
						{
							DoCast(me->getVictim(), SPELL_VOMIT_SPRAY);
							events.ScheduleEvent(EVENT_VOMIT_SPRAY, RAND(10000,15000));
						}
						return;
				}
			}

			DoMeleeAttackIfReady();
        }

	   void JustDied(Unit* pKiller)
        {
			if(!m_pInstance) return;
            m_pInstance->SetData(DATA_SUMMONS, 0);
        }
    };

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new npc_abonAI(pCreature);
    }
};

void AddSC_boss_lich_king_hr()
{
    new boss_lich_king_hor();
    new npc_raging_gnoul();
    new npc_risen_witch_doctor();
    new npc_abon();
	new npc_ghostly_priest();
    new npc_phantom_mage();
    new npc_phantom_hallucination();
    new npc_shadowy_mercenary();
    new npc_spectral_footman();
    new npc_tortured_rifleman();
}